package com.yuhuihui.common.utils.excel.export;

import com.yuhuihui.common.utils.LocalDateUtil;
import com.yuhuihui.common.utils.ObjectUtil;
import com.yuhuihui.common.utils.ThreadUtil;
import com.yuhuihui.common.utils.excel.export.parameters.ExcelExportBatchSheetParameter;
import com.yuhuihui.common.utils.excel.export.parameters.ExcelExportOneSheetParameter;
import com.yuhuihui.common.utils.excel.export.parameters.ExcelExportSheetParameter;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletResponse;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * excel导出
 *
 * @author yuhh
 * @date 2022-08-22 16:20:47
 */
public class ExcelExport {

    private static final Logger logger = LoggerFactory.getLogger(ExcelExport.class);

    /**
     * excel导出工作表线程池
     */
    private static final ThreadPoolExecutor EXCEL_EXPORT_SHEET_EXECUTOR = ThreadUtil.buildAutoCloseThreadPool("excelExportSheet");

    /**
     * excel导出行线程池
     */
    private static final ThreadPoolExecutor EXCEL_EXPORT_ROW_EXECUTOR = ThreadUtil.buildAutoCloseThreadPool("excelExportRow");

    /**
     * HSSFWorkbook最大行数
     */
    private static final int MAX_XLS_ROW = 65535;

    /**
     * 执行导出excel--单个sheet
     *
     * @param parameter excel导出单个sheet参数
     * @author yuhh
     * @date 2022-08-22 16:20:51
     */
    protected static <T> void doOneSheetExport(ExcelExportOneSheetParameter<T> parameter){
        // 校验参数
        calibrationOneSheetParameters(parameter);
        ExcelExportSheetParameter<T> sheetParameter = parameter.getSheetParameter();
        Workbook workBook = getWorkBook(sheetParameter.getDataList().size());
        // 生成sheet
        creatSheet(workBook, sheetParameter, false);
        try {
            doExport(workBook, parameter.getResponse(), parameter.getFileName());
        } catch (Exception e) {
            logger.error("excel导出单个sheet出现异常", e);
        } finally {
            try {
                workBook.close();
            } catch (IOException e) {
                logger.error("关闭excel工作区间出现异常", e);
            }
        }
    }

    /**
     * 执行导出excel--多个个sheet
     *
     * @param parameter excel导出多个sheet参数
     * @author yuhh
     * @date 2022-08-22 16:20:55
     */
    protected static <T> void doBatchSheetExport(ExcelExportBatchSheetParameter<T> parameter){
        // 校验参数
        calibrationBatchSheetParameters(parameter);
        Workbook workBook = getWorkBook(parameter.getMaxRows());
        List<ExcelExportSheetParameter<T>> parameters = parameter.getSheetParameters();
        // 生成多个sheet
        parameters.forEach(sheetParameter -> creatSheet(workBook, sheetParameter, false));
        try {
            doExport(workBook, parameter.getResponse(), parameter.getFileName());
        } catch (Exception e) {
            logger.error("excel导出多个sheet出现异常", e);
        }
    }

    /**
     * 执行导出excel--单个sheet--异步
     *
     * @param parameter excel导出单个sheet参数
     * @author yuhh
     * @date 2022-08-22 16:20:57
     */
    protected static <T> void doOneSheetExportAsync(ExcelExportOneSheetParameter<T> parameter){
        // 校验参数
        calibrationOneSheetParameters(parameter);
        ExcelExportSheetParameter<T> sheetParameter = parameter.getSheetParameter();
        Workbook workBook = getWorkBook(sheetParameter.getDataList().size());
        // 生成sheet
        creatSheet(workBook, sheetParameter, true);
        try {
            doExport(workBook, parameter.getResponse(), parameter.getFileName());
        } catch (Exception e) {
            logger.error("excel导出单个sheet出现异常", e);
        }
    }

    /**
     * 执行导出excel--多个个sheet--异步
     *
     * @param parameter excel导出多个sheet参数
     * @author yuhh
     * @date 2022-08-22 16:20:59
     */
    protected static <T> void doBatchSheetExportAsync(ExcelExportBatchSheetParameter<T> parameter){
        // 校验参数
        calibrationBatchSheetParameters(parameter);
        Workbook workBook = getWorkBook(parameter.getMaxRows());
        List<ExcelExportSheetParameter<T>> parameters = parameter.getSheetParameters();
        // 生成多个sheet
        parameters.forEach(sheetParameter -> EXCEL_EXPORT_SHEET_EXECUTOR.execute(new ExportSheetRunnable<>(workBook, sheetParameter)));
        try {
            doExport(workBook, parameter.getResponse(), parameter.getFileName());
        } catch (Exception e) {
            logger.error("excel导出多个sheet出现异常", e);
        }
    }

    /**
     * 生成sheet
     *
     * @param workBook  excel工作区间
     * @param parameter sheet参数
     * @author yuhh
     * @date 2022-08-22 16:21:05
     */
    static <T> void creatSheet(Workbook workBook, ExcelExportSheetParameter<T> parameter, boolean async){
        if (parameter.getCellStyle() == null){
            parameter.setCellStyle(getCellStyle(workBook));
        }
        Sheet sheetObj = getSheet(workBook, parameter.getSheetName());
        // 列宽自动调整
        autoColumnSizeCn(sheetObj, parameter.getFieldNames()[0]);
        // 写入标题
        writeTitle(workBook, sheetObj, parameter);
        // 列名行写入excel
        optWriteColName(sheetObj, parameter);
        try {
            // 单元格数据导出
            excelCore(sheetObj, parameter, async);
        } catch (Exception e){
            logger.error("单元格数据导出出现异常", e);
        }
        // 写入汇总信息
        writeSummary(workBook, sheetObj, parameter);
    }

    /**
     * 导出行数据
     *
     * @param sheet           床单
     * @param data            数据列表
     * @param codeSize        code长度
     * @param row             一行
     * @param replaceInfoKeys 替换值key列表
     * @param mergeLineSet    合并线集
     * @param parameter       参数
     * @param mergeLineNum    合并行数
     * @throws Exception 例外
     * @author yuhh
     * @date 2022-08-23 15:35:54
     */
    static <T> void exportRow(Sheet sheet, T data, int codeSize, int row, Set<String> replaceInfoKeys,
                              Set<String> mergeLineSet, ExcelExportSheetParameter<T> parameter, int[] mergeLineNum) throws Exception {
        String[] mergeLine = parameter.getMergeLine();
        List<String[]> mergeRow = parameter.getMergeRow();
        String[] fieldCodes = parameter.getFieldCodes();
        Map<String, Map<Object, Object>> replaceInfo = parameter.getReplaceInfo();
        List<T> dataList = parameter.getDataList();
        int startRow = parameter.getFieldNames().length;
        String fieldCode;
        T beforeData = null;
        Object value, beforeValue;
        int rowNumInt = dataList.size();
        // 判断是否需要行合并
        if (parameter.getContentRowMerger() && mergeRow != null){
            mergeRow(sheet, data, row, fieldCodes, mergeRow);
        }
        // 获取上一行内容
        if (row > startRow){
            beforeData = dataList.get(row - startRow - 1);
        }
        Row rowObj = sheet.createRow(row);
        Cell cellObj;
        CellStyle style = parameter.getCellStyle();
        for (int i = 0; i < codeSize; i++){
            fieldCode = fieldCodes[i];
            value = getValue(data, fieldCode);
            // 判断是否需要列合并
            if (parameter.getContentLineMerger() && mergeLine != null && row > startRow && mergeLineSet.contains(fieldCode)){
                beforeValue = getValue(beforeData, fieldCode);
                if (!value.equals(beforeValue)){
                    if (mergeLineNum[i] != 0){
                        // 参数一：起始行，参数二：结束行，参数三：起始列，参数四：结束列
                        sheet.addMergedRegion(new CellRangeAddress(row - mergeLineNum[i] - 1, row - 1, i, i));
                        mergeLineNum[i] = 0;
                    }
                } else {
                    mergeLineNum[i] = mergeLineNum[i] + 1;
                    if (row == rowNumInt  + startRow - 1){
                        sheet.addMergedRegion(new CellRangeAddress(row - mergeLineNum[i], row, i, i));
                    }
                }
            }
            // 判断给字段是否需要替换
            if (replaceInfoKeys != null && replaceInfoKeys.contains(fieldCode)){
                Object replaceValue = replaceInfo.get(fieldCode).get(value);
                if (!ObjectUtils.isEmpty(replaceValue)){
                    value = replaceValue;
                }
            }
            synchronized (ExcelExport.class){
                cellObj = rowObj.createCell(i);
                cellObj.setCellStyle(style);
                cellObj.setCellValue(ObjectUtil.object2String(value));
            }
        }
    }

    /**
     * 执行导出
     *
     * @param workBook excel工作区间
     * @param response response
     * @param fileName 文件名
     * @throws Exception 异常
     * @author yuhh
     * @date 2022-08-22 16:21:03
     */
    private static void doExport(Workbook workBook, HttpServletResponse response, String fileName) throws Exception{
        String fileType;
        try (OutputStream out = response.getOutputStream()){
            if (workBook instanceof XSSFWorkbook){
                fileType = ".xlsx";
            } else {
                fileType = ".xls";
            }
            // 设置响应头
            defineFileType(response, fileName, fileType);
            workBook.write(out);
        }
    }

    /**
     * 根据数据量获取相应的excel工作区间
     *
     * @param size 数据量
     * @return {@link Workbook } excel工作区间
     * @author yuhh
     * @date 2022-08-22 16:21:12
     */
    private static Workbook getWorkBook(int size){
        if (size > MAX_XLS_ROW - 1){
            return new XSSFWorkbook();
        }
        return new HSSFWorkbook();
    }

    /**
     * 获取excel工作表
     *
     * @param workBook  excel工作区间
     * @param sheetName 片区名
     * @return {@link Sheet } excel工作表
     * @author yuhh
     * @date 2022-08-22 16:21:17
     */
    private static synchronized Sheet getSheet(Workbook workBook, String sheetName){
        if (ObjectUtils.isEmpty(sheetName)){
            return workBook.createSheet();
        }
        return workBook.createSheet(sheetName);
    }

    /**
     * 根据表头自动设置列宽
     *
     * @param sheet      excel片区
     * @param fieldNames 表头名数组
     * @author yuhh
     * @date 2022-08-22 16:21:21
     */
    private static void autoColumnSizeCn(Sheet sheet, String[] fieldNames) {
        String fieldName;
        for (int i = 0, length = fieldNames.length; i < length; i++) {
            fieldName = fieldNames[i];
            sheet.setColumnWidth(i, fieldName.getBytes().length * 2 * 256);
        }
    }

    /**
     * 写入标题
     *
     * @param workBook  excel工作区间
     * @param sheet     excel片区
     * @param parameter sheet参数
     * @author yuhh
     * @date 2025-05-16 14:21:36
     */
    private static <T> void writeTitle(Workbook workBook, Sheet sheet, ExcelExportSheetParameter<T> parameter) {
        String[] fieldCodes = parameter.getFieldCodes();
        String title = parameter.getTitle();
        if (!ObjectUtils.isEmpty(title)){
            setCellInfo(title, workBook, sheet,fieldCodes.length - 1, 0, true);
        }
    }

    /**
     * 将取到的列名写入excel中作为题头
     *
     * @param sheet     excel片区
     * @param parameter sheet参数
     * @author yuhh
     * @date 2022-08-22 16:55:50
     */
    private static <T> void optWriteColName(Sheet sheet, ExcelExportSheetParameter<T> parameter) {
        String[] mergeLine = parameter.getMergeLine();
        List<String[]> mergeRow = parameter.getMergeRow();
        String[][] fieldNames = parameter.getFieldNames();
        String[] fieldCodes = parameter.getFieldCodes();
        // 如果有标题，则表头从第二行开始；如果没有，则从第一行开始
        int startRow = ObjectUtils.isEmpty(parameter.getTitle())? 0 : 1;
        if (mergeRow != null){
            String beforeCode, endCode;
            for (String[] merge: mergeRow){
                if (merge.length != 2){
                    throw new RuntimeException("导出excel时，合并标题行数据格式有问题");
                }
                beforeCode = merge[0];
                endCode = merge[1];
                for (int i = 0, length = fieldNames.length; i < length; i++){
                    if (isMergeHead(fieldCodes, fieldNames[i], beforeCode, endCode)){
                        handleMergeRow(sheet, fieldCodes, beforeCode, endCode, i + startRow);
                    }
                }
            }
        }
        Set<String> mergeLineSet = mergeLine != null? Arrays.stream(mergeLine).collect(Collectors.toSet()) : null;
        String fieldCode, beforeValue, value;
        int[] mergeLineNum = new int[fieldCodes.length];
        Cell cellObj;
        CellStyle style = parameter.getCellStyle();
        for (int i = 0, length = fieldNames.length; i < length; i++){
            Row rowObj = sheet.createRow(i + startRow);
            for (int col = 0, colNum = fieldNames[i].length; col < colNum; col++) {
                fieldCode = fieldCodes[col];
                value = fieldNames[i][col];
                // 判断是否需要列合并
                if (mergeLine != null && i > 0 && mergeLineSet.contains(fieldCode)){
                    beforeValue = fieldNames[i - 1][col];
                    if (!value.equals(beforeValue)){
                        if (mergeLineNum[col] != 0){
                            // 参数一：起始行，参数二：结束行，参数三：起始列，参数四：结束列
                            sheet.addMergedRegion(new CellRangeAddress(i - mergeLineNum[i] + startRow, i + startRow, col, col));
                            mergeLineNum[col] = 0;
                        }
                    } else {
                        mergeLineNum[col] = mergeLineNum[col] + 1;
                        if (i == length - 1){
                            sheet.addMergedRegion(new CellRangeAddress(i - mergeLineNum[col] + startRow, i + startRow, col, col));
                        }
                    }
                }
                cellObj = rowObj.createCell(col, CellType.STRING);
                cellObj.setCellStyle(style);
                cellObj.setCellValue(value);
            }
        }
    }

    /**
     * excel导入数据的主函数
     *
     * @param sheet     excel片区
     * @param parameter sheet参数
     * @throws Exception 异常
     * @author yuhh
     * @date 2022-08-22 17:24:19
     */
    private static <T> void excelCore(Sheet sheet, ExcelExportSheetParameter<T> parameter, boolean async) throws Exception {
        String[] mergeLine = parameter.getMergeLine();
        Map<String, Map<Object, Object>> replaceInfo = parameter.getReplaceInfo();
        List<T> dataList = parameter.getDataList();
        int startRow = parameter.getFieldNames().length;
        if (!ObjectUtils.isEmpty(parameter.getTitle())){
            startRow = startRow + 1;
        }
        int codeSize = parameter.getFieldCodes().length;
        Set<String> replaceInfoKeys = replaceInfo != null? replaceInfo.keySet(): null;
        Set<String> mergeLineSet = mergeLine != null? Arrays.stream(mergeLine).collect(Collectors.toSet()) : null;
        int[] mergeLineNum = new int[codeSize];
        for (int row = startRow; row < dataList.size() + startRow; row++) {
            if (async){
                parameter.setContentLineMerger(false);
                EXCEL_EXPORT_ROW_EXECUTOR.execute(new ExportRowRunnable<T>(sheet, dataList.get(row - startRow), codeSize, row, replaceInfoKeys, mergeLineSet, parameter));
            } else {
                exportRow(sheet, dataList.get(row - startRow), codeSize, row, replaceInfoKeys, mergeLineSet, parameter, mergeLineNum);
            }
        }
    }

    /**
     * 写入汇总信息
     *
     * @param workBook  excel工作区间
     * @param sheet     excel片区
     * @param parameter sheet参数
     * @author yuhh
     * @date 2025-05-16 14:24:36
     */
    private static <T> void writeSummary(Workbook workBook, Sheet sheet, ExcelExportSheetParameter<T> parameter) {
        String[] fieldCodes = parameter.getFieldCodes();
        String summary = parameter.getSummary();
        if (!ObjectUtils.isEmpty(summary)){
            setCellInfo(summary, workBook, sheet, fieldCodes.length - 1, sheet.getLastRowNum() + 1, false);
        }
    }

    /**
     * 设置响应头
     *
     * @param response       请求响应
     * @param exportFileName 文件名
     * @author yuhh
     * @date 2022-08-22 16:21:32
     */
    private static void defineFileType(HttpServletResponse response, String exportFileName, String fileType) {
        // 清空输出流
        response.reset();
        String fileName = "";
        try {
            fileName = new String(exportFileName.getBytes("gb2312"), "ISO8859-1")
                    + "-" + LocalDateUtil.getFormatDate(new Date(), "yyyyMMdd-HHmm");
        } catch (UnsupportedEncodingException e) {
            logger.error("导出excel时设置响应头出现异常", e);
        }
        // 设定输出文件头
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + "." + fileType);
        // 定义输出类型
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
    }

    /**
     * 校验excel导出参数--单个sheet
     *
     * @param parameter excel单个sheet导出参数
     * @author yuhh
     * @date 2022-08-22 16:21:36
     */
    private static <T> void calibrationOneSheetParameters(ExcelExportOneSheetParameter<T> parameter){
        Assert.notNull(parameter.getResponse(), "excel导出response为null");
        Assert.notNull(parameter.getFileName(), "excel导出文件名为null");
        calibrationSheetParameter(parameter.getSheetParameter());
    }

    /**
     * 校验excel导出参数--单个sheet
     *
     * @param parameter excel多个个sheet导出参数
     * @author yuhh
     * @date 2022-08-22 16:21:38
     */
    private static <T> void calibrationBatchSheetParameters(ExcelExportBatchSheetParameter<T> parameter){
        Assert.notNull(parameter.getResponse(), "excel导出response为null");
        Assert.notNull(parameter.getFileName(), "excel导出文件名为null");
        List<ExcelExportSheetParameter<T>> parameters = parameter.getSheetParameters();
        Assert.notEmpty(parameters, "excel多个sheet批量导出的sheet参数为null或为空");
        parameters.forEach(ExcelExport::calibrationSheetParameter);
        parameter.setMaxRows(
                parameters.stream().max((a, b) -> Math.max(a.getDataList().size(), b.getDataList().size()))
                .map(excelExportSheetParameter -> excelExportSheetParameter.getDataList().size()).orElse(0)
        );
    }

    /**
     * 校验sheet参数
     *
     * @param parameter sheet参数
     * @author yuhh
     * @date 2022-08-22 16:21:40
     */
    private static <T> void calibrationSheetParameter(ExcelExportSheetParameter<T> parameter){
        Assert.notNull(parameter.getDataList(), "excel导出数据列表为null");
        Assert.notEmpty(parameter.getFieldNames(), "excel导出表头为null或为空");
        Assert.notEmpty(parameter.getFieldCodes(), "excel导出表头的code为null或为空");
        Assert.isTrue(parameter.getFieldNames()[0].length == parameter.getFieldCodes().length,
                "excel导出表头长度和表头的code长度不符，表头长度为：" + parameter.getFieldNames()[0].length
                        + "，表头的code长度为：" + parameter.getFieldCodes().length);
    }

    /**
     * 是否需要合并表头
     *
     * @param fieldCodes 列表code
     * @param fieldNames 表头名数组
     * @param beforeCode 合并开始code
     * @param endCode    合并结束code
     * @return boolean
     * @author yuhh
     * @date 2022-08-22 16:52:58
     */
    private static boolean isMergeHead(String[] fieldCodes, String[] fieldNames, String beforeCode,  String endCode){
        String beforeName = null, endName = null;
        for (int i = 0, length = fieldCodes.length; i < length; i++ ){
            if (fieldCodes[i].equals(beforeCode)){
                beforeName = fieldNames[i];
                continue;
            }
            if (fieldCodes[i].equals(endCode)){
                endName = fieldNames[i];
                continue;
            }
            if (beforeName != null && endName != null){
                break;
            }
        }
        return beforeName != null && beforeName.equals(endName);
    }

    /**
     * 执行合并行
     *
     * @param sheet      excel片区
     * @param fieldCodes 列表code
     * @param beforeCode 合并开始code
     * @param endCode    合并结束code
     * @param row        需要合并的行下标
     * @author yuhh
     * @date 2022-08-22 16:53:38
     */
    private static void handleMergeRow(Sheet sheet, String[] fieldCodes, String beforeCode,  String endCode, int row){
        int beforeIndex = -1, endIndex = -1;
        for (int i = 0, length = fieldCodes.length; i < length; i++ ){
            if (fieldCodes[i].equals(beforeCode)){
                beforeIndex = i;
            }
            if (fieldCodes[i].equals(endCode)){
                endIndex = i;
            }
        }
        if (beforeIndex == -1 || endIndex == -1){
            return;
        }
        // 参数一：起始行，参数二：结束行，参数三：起始列，参数四：结束列
        sheet.addMergedRegion(new CellRangeAddress(row, row, beforeIndex, endIndex));
    }

    /**
     * 合并行
     *
     * @param sheet      excel片区
     * @param data       数据
     * @param row        行编码
     * @param fieldCodes 列表code
     * @param mergeRow   需要合并的行对应的code
     * @throws IntrospectionException    内省异常
     * @throws InvocationTargetException 调用目标异常
     * @throws IllegalAccessException    非法访问异常
     * @author yuhh
     * @date 2022-08-22 17:05:39
     */
    private static <T> void mergeRow(Sheet sheet, T data, int row, String[] fieldCodes, List<String[]> mergeRow) throws IntrospectionException, InvocationTargetException, IllegalAccessException{
        String beforeCode, endCode;
        for (String[] merge: mergeRow){
            if (merge.length != 2){
                throw new RuntimeException("导出excel时，合并行数据格式有问题");
            }
            beforeCode = merge[0];
            endCode = merge[1];
            if (getValue(data, beforeCode).equals(getValue(data, endCode))){
                handleMergeRow(sheet, fieldCodes, beforeCode, endCode, row);
            }
        }
    }

    /**
     * 获取单元格值
     *
     * @param data      行数据
     * @param fieldCode 列表code
     * @return {@link Object }
     * @throws IntrospectionException    内省异常
     * @throws InvocationTargetException 调用目标异常
     * @throws IllegalAccessException    非法访问异常
     * @author yuhh
     * @date 2022-08-22 17:06:10
     */
    private static <T> Object getValue(T data, String fieldCode) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
        if (data instanceof Map){
            return ((Map) data).get(fieldCode);
        }
        // 根据code获取相应属性的set/get方法
        PropertyDescriptor pd = new PropertyDescriptor(fieldCode, data.getClass());
        // 获取get方法
        Method method = pd.getReadMethod();
        // 获取相应的值
        return method.invoke(data);
    }

    /**
     * 获取单元格样式
     *
     * @param wbObj 工作区间
     * @return {@link CellStyle }
     * @author yuhh
     * @date 2022-08-23 15:59:13
     */
    private static CellStyle getCellStyle(Workbook wbObj){
        CellStyle style = wbObj.createCellStyle();
        //垂直居中
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        //水平居中
        style.setAlignment(HorizontalAlignment.CENTER);
        return style;
    }

    /**
     * 合并并设置单元格信息
     *
     * @param info      信息
     * @param workBook     WB对象
     * @param sheet  工作表obj
     * @param lastCol   最后一个单元格下标
     * @param rowNum    行号
     * @param alignment 是否要左右居中
     * @author yuhh
     * @date 2025-05-16 14:18:06
     */
    private static void setCellInfo(String info, Workbook workBook, Sheet sheet, int lastCol, int rowNum, boolean alignment){
        Row row = sheet.createRow(rowNum);
        Cell cell = row.createCell(0);
        cell.setCellValue(info);
        sheet.addMergedRegion(new CellRangeAddress(rowNum, rowNum,0, lastCol));
        CellStyle style = workBook.createCellStyle();
        //垂直居中
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        // 左右居中
        if (alignment){
            style.setAlignment(HorizontalAlignment.CENTER);
        }
        cell.setCellStyle(style);
    }
}
